package jmysql;

import java.io.FileInputStream;
import java.io.IOException;
import java.sql.ResultSet;
import java.util.Properties;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.HashMap;
import java.util.Map;

/**
 *
 * @author cmerino
 * @version 1.0
 * 
 */
public class classMySQL {

    /***                     ATRIBUTOS PÚBLICOS                        ***/
    /***                     ATRIBUTOS PRIVADOS                        ***/
    private String __DATABASE_NAME;
    private String __LOGIN;
    private String __PASSWORD;
    private String __JDBC_URL;
    private String __PROP_PATH;
    private ResultSet __RESULT;
    private Map<String, Class<?>> __MAP_FIELDS;
    private Map<String, Map<Integer, Object>> __MAP_VALUES;

    /*********************************************************************/
    /***                       MÉTODOS PÚBLICOS                        ***/
    /*********************************************************************/
    
    
    
    
    /**
     * 
     * Constructor de la Clase <b>classMySQL</b>.
     * Recibe como parámetro la ruta de la propertie y de ahí define los 
     * atributos de: nombre de la base de datos, la url del JDBC, el nombre
     * de usuario y el password con el cual se conectará.
     * 
     * @param strProperties Definie la ruta en que se encuentra la propertie
     *                      a la que debe acceder la clase para obtener los
     *                      atributos de configuración.
     * 
     * @exception IOException Excepción lanzada en caso de existir algún error
     *                      al intentar leer la propertie asignada.
     * 
     * 
     */
    public classMySQL(String strProperties) throws IOException {
        this.__PROP_PATH = strProperties;
        try {
            Properties props = new Properties();
            props.load(new FileInputStream(this.__PROP_PATH));
            this.__DATABASE_NAME = props.getProperty("prop_database");
            this.__JDBC_URL = props.getProperty("prop_jdbc_url") + this.__DATABASE_NAME;
            this.__LOGIN = props.getProperty("prop_login");
            this.__PASSWORD = props.getProperty("prop_password");
            this.__MAP_FIELDS = new HashMap<String, Class<?>>();
            this.__MAP_VALUES = new HashMap<String, Map<Integer, Object>>();

            /*
             * NOTA: Aún falta revisar la aplicabilidad del atributo __MAP_FIELDS
             *       respecto de su función de almacenaje del tipo de dato que
             *       contendrá el campo.
             */

        } catch (IOException ex) {
            throw (ex);
        }
    }   /* FIN public classMySQL */


    
    /**
     * 
     * Método a través del cual se realizan consultas a la base de datos. Este
     * método ejecuta la ordes SQL y su resultado lo guarda en un ResultSet
     * 
     * 
     * @param strSQL Corresponde al String de consulta SQL que recibe para
     *               ejecutar en la base de datos.
     *               En el caso de este método, el String SQL debe ser del tipo
     *               consulta "SELECT".
     * 
     * @throws SQLException Excepción lanzada en caso de ocurrir un problema al
     *               intentar conectarse a la base de datos.
     * 
     * @throws ClassNotFoundException Excepción lanzada en caso de ocurrir un 
     *               problema al intentar ejecutar la consulta a la base de datos.
     */
    public void consultaSQL(String strSQL) throws Exception {
        Connection conn = null;
        try {
            Class.forName("com.mysql.jdbc.Driver").newInstance();
            conn = DriverManager.getConnection(
                    this.__JDBC_URL,
                    this.__LOGIN,
                    this.__PASSWORD);
            if (conn != null) {
                Statement s = conn.createStatement();
                this.__RESULT = s.executeQuery(strSQL);
                s.close();
            }
        } catch (SQLException ex) {
            throw (ex);
        } catch (ClassNotFoundException ex) {
            throw (ex);
        } finally {
            conn.close();
        }
        
    }   /* FIN public void consultaSQL */
    

    
    /**
     * 
     * Método a través del cual se ejecuta una acción sobre la base de datos.
     * El objeto de este método es ejecutar una acción que no requiera la
     * devolución de un ResultSet.
     * 
     * 
     * @param strSQL Corresponde al String SQL que recibe para ejecutar la 
     *               acción sobre la base de datos.
     *               En el caso de este método, el String SQL no debe ser del 
     *               tipo consulta "SELECT", ya que no está diseñado para
     *               retornar valor alguno.
     * 
     * @throws SQLException Excepción lanzada en caso de ocurrir un problema al
     *               intentar conectarse a la base de datos.
     * 
     * @throws ClassNotFoundException Excepción lanzada en caso de ocurrir un 
     *               problema al intentar ejecutar la instrucción SQL.
     */
    public void ordenSQL(String strSQL) throws Exception {
        Connection conn = null;
        try {
            Class.forName("com.mysql.jdbc.Driver").newInstance();
            conn = DriverManager.getConnection(
                    this.__JDBC_URL,
                    this.__LOGIN,
                    this.__PASSWORD);
            if (conn != null) {
                Statement s = conn.createStatement();
                s.executeUpdate(strSQL);
                s.close();
            }
        } catch (SQLException ex) {
            throw (ex);
        } catch (ClassNotFoundException ex) {
            throw (ex);
        } finally {
            conn.close();
        }
        
    }   /* FIN public void ordenSQL */

    
    
    /**
     * 
     * Método que devuelve el nombre de la base de datos con la cual interactúa 
     * la conexión.
     * 
     * @return Un objeto String con el nombre de la base de datos.
     */
    public String getDatabaseName() {
        return __DATABASE_NAME;
    }   /* FIN public String getDatabaseName */
    
    

    /**
     * 
     * Método que permite forzar la definición del nombre de la base de datos
     * con la cual interacuará la conexión. 
     * En caso de no ser invocado este método, el nombre de la base de datos
     * será obtenido desde la propertie asignada en el constructor.
     * 
     * @param __DATABASE_NAME Un objeto String con el nombre de la base de datos.
     */
    public void setDatabaseName(String __DATABASE_NAME) {
        this.__DATABASE_NAME = __DATABASE_NAME;
    }   /* FIN public void setDatabaseName */
    

    
    /**
     * 
     * Método que devuelve el PATH o URL en que se encuentra el JDBC que se
     * emplea para la comunicación con la base de datos.
     * 
     * @return Un objeto String con el PATH o URL de la ubicación del driver 
     *         JDBC
     */
    public String getJDBC_URL() {
        return __JDBC_URL;
    }   /* FIN public String getJDBC_URL */

    
    
    /**
     * 
     * Método que dérmite forzar la definición del PATH o URL en que se 
     * encuentra el JDBC utilizado para la comunicación con la base de datos.
     * En caso de no ser invocado este método, dicha URL será obtenida desde 
     * la propertie asignada en el constructor.
     * 
     * @param __JDBC_URL Un objeto String con el PATH o URL de la ubicación 
     *                   del driver JDBC
     */
    public void setJDBC_URL(String __JDBC_URL) {
        this.__JDBC_URL = __JDBC_URL;
    }   /* FIN public void setJDBC_URL */
    
    
    
    /**
     * 
     * Método que devuelve el nombre del usuario empleado para establecer la
     * conexión con la base de datos.
     * 
     * @return Un objeto String con el nombre de usuario empleado para
     *         establecer la conexión a la base de datos.
     */
    public String getUserName() {
        return __LOGIN;
    }   /* FIN public String getUserName */
    
    
    
    /**
     * 
     * Método que permite forzar la definición del nombre del usuario que 
     * será empleado para establecer la conexión con la base de datos.
     * En caso de no ser invocado este método, dicho nombre de usuario 
     * será obtenido desde la propertie asignada en el constructor.
     * 
     * @param __LOGIN Un objeto String con el nombre de usuario empleado para
     *                establecer la conexión a la base de datos.
     */
    public void setUserName(String __LOGIN) {
        this.__LOGIN = __LOGIN;
    }   /* FIN public void setUserName */
    
    
    
    /**
     * 
     * Método que devuelve el password del usuario empleado para establecer la
     * conexión con la base de datos.
     * 
     * @return Un objeto String con el password asociado al usuario empleado
     *         para establecer la conexión a la base de datos.
     */
    public String getPassword() {
        return __PASSWORD;
    }   /* FIN public String getPassword */
    
    
    
    /**
     * 
     * Método que permite forzar la definición del password del usuario que 
     * será empleado para establecer la conexión con la base de datos.
     * En caso de no ser invocado este método, dicha password será obtenida 
     * desde la propertie asignada en el constructor.
     * 
     * @param __PASSWORD Un objeto String con el password asociado al usuario 
     *         empleado para establecer la conexión a la base de datos.
     */
    public void setPassword(String __PASSWORD) {
        this.__PASSWORD = __PASSWORD;
    }   /* FIN public void setPassword */
    
    
    
    /**
     * 
     * Método que devuelve el path de la propertie que contiene los parámetros 
     * por defecto que se emplearán en el establecimiento de la conexión con 
     * la base de datos.
     * 
     * @return Un objeto String con el PATH o URL del arhivo propertie
     *         empleado para definir la configuración de conexión por defecto.
     */
    public String getPropertiePath() {
        return __PROP_PATH;
    }   /*  FIN public String getPropertiePath */
    
    
    
    /**
     * 
     * Método que permite definir el path de la propertie que contiene los 
     * parámetros por defecto que se emplearán en el establecimiento de la 
     * conexión con la base de datos.
     * 
     * @param __PROP_PATH Un objeto String con el PATH o URL del arhivo propertie
     *         empleado para definir la configuración de conexión por defecto.
     */
    public void setPropertiePath(String __PROP_PATH) {
        this.__PROP_PATH = __PROP_PATH;
    }   /* FIN public void setPropertiePath*/
    
    
    
    /**
     * 
     * Método que devuelve el ResultSet obtenido como resultado de una consulta
     * sobre la base de datos.
     * 
     * @return El ResultSet obtenido producto de la ejecución de la consulta
     *         a la base de datos.
     */
    public ResultSet getResultSet() {
        return this.__RESULT;
    }   /* FIN public ResultSet getResultSet */
    
    
    
    /**
     * 
     * Método que permite agregar un campo a mapear para una consulta de 
     * la base de datos. Éste, internamente, crea una definición del campo y
     * una estructura de mapa de los datos asociados al campo definido.
     * 
     * @param fldName Corresponde al nombre del campo que se desea egregar.
     * @param fldClass Corresponde al tipo de dato con el cual se definirá el
     *                campo.
     */
    public void addField(String fldName, Class<?> fldClass) {

        //    Se agrega el campo en el map de campos.
        __MAP_FIELDS.put(fldName, fldClass);

        //    Se crea y agrega el vector que contendrá los registros
        //  del campo creado.
        __MAP_VALUES.put(fldName, new HashMap<Integer, Object>());

    }   /* FIN public void addField */
    
    
    
    /**
     * 
     * Método que permite remover un campo asociado a la estructura de mapeo
     * definida para la consulta.
     * 
     * @param fldName Corresponde al nombre del campo que se desea remover.
     */
    public void removeField(String fldName) {
        this.__MAP_FIELDS.remove(fldName);
    }   /* FIN public void removeField */
    
    
    
    /**
     * 
     * Método que remueve todos los campos asociados a la estructura de mapeo
     * definida para la consulta.
     * 
     */
    public void clearFields() {
        this.__MAP_FIELDS.clear();
        this.__MAP_VALUES.clear();  // Tal vez sea necesario recorrer los objetos
                                    // contenidos en el map para efectuar su 
                                    // limpieza.
    }   /* FIN public void clearFields */
    
    
    
    
    /**
     * 
     * Método que permite obtener el tipo de objeto al que corresponde un 
     * determinado campo agregado a la consulta.
     * 
     * @param fldName Corresponde al nombre del campo indicado.
     * @return Un objeto tipo <code>Class<?></code> que indica el tipo de dato.
     */
    public Class<?> getFieldType(String fldName) {
        return this.__MAP_FIELDS.get(fldName);
    }   /* FIN public Class<?> getFieldType */
    
    
    
    /**
     * 
     * Método que permite agregar un registro al mapa de datos para un campo
     * específico.
     * 
     * @param recKey Valor entero único en el mapa, el cual corresponde a la
     *               clave asignada al registro.
     * @param fldName Valor String que corresponde al nombre del campo sobre el
     *               cual se realizará la inserción del registro en el mapa.
     * @param recObj Objeto correspondiente al registro a insertar.
     */
    public void addRecord(Integer recKey, String fldName, Object recObj) {
        Map<Integer, Object> m = this.__MAP_VALUES.get(fldName);
        m.put(recKey, recObj);
    }   /* FIN public void addRecord */
    
    
    
    /**
     * 
     * Método que devuelce un registro al mapa de datos para un campo y llave
     * específicos.
     * 
     * @param recKey Valor entero único en el mapa, el cual corresponde a la
     *               clave asignada al registro.
     * @param fldName Valor String que corresponde al nombre del campo sobre el
     *               cual se realizará la búsqueda del registro en el mapa.
     * @return El objeto correspondiente al registro dentro del mapa.
     */
    public Object getRecord(Integer recKey, String fldName) {
        Map<Integer, Object> m = this.__MAP_VALUES.get(fldName);
        return m.get(recKey);
    }   /* FIN public Object getRecord */
}
